package online

import (
	"server/common"

	"github.com/mitchellh/mapstructure"
)

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) validateNegotiateRes(msg common.Message) (
	*UserInfo, *common.AppError) {
	var resData common.ResMsgData
	if err := mapstructure.Decode(msg.Data, &resData); err != nil {
		errStr := "invalid message data format for negotiate response: " + err.Error()
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithError(err).Error(errStr)
		return nil, &common.AppError{ErrCode: 400, ErrMsg: errStr}
	}

	if resData.Code != 0 {
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error("negotiate response failed: " + resData.Msg)
	} else {
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Info("negotiate response successful")
	}

	var negotiateData common.P2PNegotiateResData
	if err := mapstructure.Decode(resData.Data, &negotiateData); err != nil {
		errStr := "invalid negotiate response data format: " + err.Error()
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithError(err).Error(errStr)
		return nil, &common.AppError{ErrCode: 500, ErrMsg: errStr}
	}

	if negotiateData.CallId == "" {
		errStr := "callId is empty in negotiate response"
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(errStr)
		return nil, &common.AppError{ErrCode: 400, ErrMsg: errStr}
	}

	call, exists := s.callMgr.GetCall(negotiateData.CallId)
	if !exists {
		err := common.AppError{ErrCode: 404, ErrMsg: "call does not exist"}
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(err)
		return nil, &err
	}

	if call.CallState != Negotiating {
		err := common.AppError{ErrCode: 400, ErrMsg: "call is not in negotiating state"}
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(err)
		return nil, &err
	}

	var peerUserId string
	switch negotiateData.UserId {
	case call.CallerId:
		peerUserId = call.CalleeId
	case call.CalleeId:
		peerUserId = call.CallerId
	default:
		errStr := "userId does not match call participants: caller " + call.CallerId + ", callee " + call.CalleeId
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(errStr)
		return nil, &common.AppError{ErrCode: 400, ErrMsg: errStr}
	}

	peerClient := s.userMgr.GetUserByUserId(peerUserId)
	if peerClient == nil {
		err := common.AppError{ErrCode: 404, ErrMsg: "peer client not found"}
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(err)
		return nil, &err
	}

	return peerClient, nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) handleNegotiateRes(msg common.Message, from common.ServiceInfo,
	connId string) error {
	common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithFields(map[string]interface{}{
		"msg":    common.ToJson(msg),
		"from":   from,
		"connId": connId,
	}).Info("call handleNegotiateRes")

	peerClient, appErr := s.validateNegotiateRes(msg)
	if appErr != nil {
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithError(appErr).
			Error("failed to validate negotiate response")
		return nil
	}

	if err := s.dispatcher.Dispatch(msg, s.serviceInfo, common.ServiceInfo{
		ServiceType: common.SERVICE_TYPE_ACCESS,
	}, peerClient.ConnId); err != nil {
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithError(err).
			Error("failed to send negotiate response")
	}

	return nil
}
